# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-03-31 15:10:31
# @Last Modified by:   bao
# @Last Modified time: 2017-05-01 14:41:00
import sys
sys.path.append("..")

import re
import random
from CoreSim.MScheduler import *
from CoreSim.Flow import *
from CoreSim.Event import *
from CoreSim.Link import *
from CoreSim.Unit import *

def assignNewMulticastFlow(srcHost, dstHosts, flows, flowSize, startTime, mcastFlowId, numHosts, numToRs, numCores):
    # multicast flow
    flow = Flow()
    flow.startId = srcHost
    flow.endId = dstHosts
    flow.SetFlowSize(flowSize)
    flow.startTime = startTime
    flow.flowId = len(flows) + 1
    flow.mcastFlowId = mcastFlowId
    flows.append(flow)
    
    # assign path
    srcToRId = GetToRId(srcHost, numHosts)
    srcToRNodeId = ConvertToNodeId(srcToRId, ToR, numHosts*numToRs, numToRs)
    
    pathLinkIds = []
    pathLinkIds.append((srcHost, srcToRNodeId))
    for dstHost in dstHosts:
        dstToRId = GetToRId(dstHost, numHosts)
        dstToRNodeId = ConvertToNodeId(dstToRId, ToR, numHosts*numToRs, numToRs)
        pathLinkIds.append((dstToRNodeId, dstHost))
        if dstToRNodeId != srcToRNodeId:
            coreId = random.randint(1, numCores)
            coreNodeId = ConvertToNodeId(coreId, Core, numHosts*numToRs, numToRs)
            if (srcToRNodeId, coreNodeId) not in pathLinkIds:
                pathLinkIds.append((srcToRNodeId, coreNodeId))
            if (coreNodeId, dstToRNodeId) not in pathLinkIds:
                pathLinkIds.append((coreNodeId, dstToRNodeId))

    flow.pathLinkIds = pathLinkIds
    flow.pathLength = 4
    return flow

class MSchedulerEPSMulticast(MScheduler):
    '''
    Multicast scheduler
    Constructing multicast tree
    '''
    def __init__(self, topo_args, flow_args, conf_args):
        MScheduler.__init__(self, topo_args, flow_args, conf_args)
        self.type = 'EPS'
    
    def InitialMulticast(self, eventQueue):
        for mflowId in self.mcastFlows:
            mcastFlow = self.mcastFlows[mflowId]
            srcHost = mcastFlow.srcHost
            dstHosts = mcastFlow.dstHosts.keys()
            
            flow = assignNewMulticastFlow(srcHost, dstHosts, self.flows, mcastFlow.flowSize, mcastFlow.startTime, \
                mcastFlow.flowId, self.numHosts, self.numToRs, self.numCores)

            # eventqueue
            event = FlowArrivalEvent(mcastFlow.startTime, flow)
            eventQueue.push(event, mcastFlow.startTime)

    def MulticastFlowArrival(self, curFlow, curTime, eventQueue):
        pathInLink = curFlow.pathLinkIds
        for link_id in pathInLink:
            link = self.Links[link_id]
            if curFlow.flowId not in link.flowIds:
                link.flowIds.append(curFlow.flowId)

    def UpdateMulticast(self, mflowId, curFlow, curTime, eventqueue):
        mcastFlow = self.mcastFlows[mflowId]
        mcastFlow.finishTime = curTime
        mcastFlow.energy += curFlow.energy
        print 'mcastFlow:%s transmission time %s energy %s'%(mflowId, mcastFlow.finishTime-mcastFlow.startTime, mcastFlow.energy)
